/*
   file : shm_interface.c
   本文件是共享内存操作相关的一些函数
   位于shm.c之上，词法语法分析器、锁模块均调用本文件中的函数
   University of Jiangsu
   memoryDB      2010.6
   
 */


#include "shm_interface.h"
#include "shm.h"
#include "lock.h"


/*======================DB===========================*/
/*
   创建数据库函数,与用户进行交互
 */
int create_db()
{
	if(ptr != NULL){
		printf("there is a database in open now,please remove it first\n");
		return -1;
	}

	char db_name[MAX_NAME];
	char pwd[MAX_PWD];
	unsigned long db_size;

	printf("name:");
	fgets(db_name,MAX_NAME,stdin);
	db_name[strlen(db_name)-1]=0;
	if(_check_input(reg_name,db_name) != 0){
		printf("input error:illegal db_name\n");
		return -1;
	}

	printf("password:");
	fgets(pwd,MAX_PWD,stdin);
	pwd[strlen(pwd)-1]=0;
	if(_check_input(reg_pwd,pwd)){
		printf("input error:illegal password\n");
		return -1;
	}
	
	printf("size(KB):");
	char size[50];
	fgets(size,50,stdin);
	size[strlen(size)-1]=0;
	if(_check_input("[0-9]+",size)){
		printf("input error:illegal db size\n");
		return -1;
	}
	db_size=atol(size);
	db_size *= 1024;
	if(db_size <= 0){
		printf("input error:db size must > 0\n");
		return -1;
	}

	int shm_errno;
	if((shm_errno=_get_init_shm( SIZE_ALL_ZONE + db_size)) ==-1){
		printf("fail to create shmm\n");
		return -2;
	}
	else if(shm_errno==-2){
		printf("fail to binding shmm\n");
		return -3;
	}

	_insert_db_info(db_name,db_size+SIZE_ALL_ZONE,pwd);
	printf("create database ok\n");
	return 0;
}

/*
   打开数据库函数,与用户进行交互
 */
int open_db()
{
	if(ptr != NULL){
		printf("there is a database in open now,please remove it first\n");
		return -1;
	}

	DIR *dp;
	struct dirent *dirp;
	if((dp=opendir("db_files/"))==NULL){
		printf("cannot open db_files\n");
		return -2;
	}
	
	int flag_db=0,i=1;;
	printf("db list:\n");
	while((dirp=readdir(dp))!=NULL){
		if(!strcmp(dirp->d_name,".") || !strcmp(dirp->d_name,".."))
		  continue;
		flag_db=1;
		printf("%d.%s\n",i++,dirp->d_name);
	}
	if(!flag_db){
		printf("no db on disk\n");
		return -2;
	}
	else
	  printf("which:");
	
	char db_name[MAX_NAME];
	fgets(db_name,MAX_NAME,stdin);
	db_name[strlen(db_name)-1]=0;
	int flag=0;
	if((dp=opendir("db_files/"))==NULL){
		printf("cannot open db_files\n");
		return -2;
	}
	while((dirp=readdir(dp))!=NULL){
		if(!strcmp(dirp->d_name,".") || !strcmp(dirp->d_name,".."))
		  continue;
		if(!strcmp(dirp->d_name,db_name)){
			flag=1;
			break;
		}
	}
	if(!flag){
		printf("no such db:%s\n",db_name);
		return -3;
	}

	char pwd[MAX_PWD];
	printf("password:");
    fgets(pwd,MAX_PWD,stdin);
	pwd[strlen(pwd)-1]=0;
	if(load_dict_and_init(db_name,pwd)){
		remove_db();    //如果插入失败，则删除已经创建的共享内存
		return -4;
	}
        
	TB *p_temp_tb=ptr_tb;
	for(i=0;i!=NUM_OF_TB;i++,p_temp_tb++){
		if(p_temp_tb->id == -1)
		  continue;
		load_record(p_temp_tb->id);
	}

	printf("open database ok\n");
	return 0;
}


/*
   删除数据库函数，与用户进行交互
 */
int delete_db()
{
	if(ptr == NULL){
		printf("no database in open\n");
		return -1;
	}

	printf("confirm?y/n");
	char a;
	scanf("%c",&a);
	getchar();
	if(a=='y'||a=='Y'){
		char  name[MAX_COMMAND];
		strcpy(name,db_dir);
		strcat(name,ptr_db->db_name); 
		
		shmdt(ptr);
		shmctl(shmid,IPC_RMID,0);
		ptr=NULL;
		printf("remove shm ok\n");
		rm_files(name);
		printf("remove files ok\n");
		return 0;
	}else
	  printf("cancled it\n");
	return -2;
}


/*==============================Table======================*/

/*
   给定Table结构,创建表及其列,成功返回1
 */
int CreateTable(Table *T)
{
	if(T->Field_num > NUM_OF_COLU)
	  return -1;  //如果所要创建的列的数目大于MAX_OF_COLU，则返回-1
	
	int tb_id=_insert_tb_info(T->Table_Name,T->Field_num);  //在第二区域中创建表结构
    
	Table_lock *tl = malloc(sizeof(Table_lock));
    Init_Table_lock(tl);
    strcpy(tl->Table_Name,T->Table_Name);
	Add_Table_lock(tl);

	/*根据给定的列信息,创建列*/
	Field *field_curr;
	field_curr=T->Field_list;
	int type;
	
	while(field_curr != NULL){
		if(field_curr->date_type==STRINGX)
		  type=0;
		else if(field_curr->date_type==INTX)
		  type=1;
		else
		  type=2;
		_insert_colu_info(tb_id,field_curr->Field_Name,field_curr->str_len,type);
		field_curr = field_curr->Next_Field;
	}

	TB *p_temp_tb=ptr_tb + tb_id;
	p_temp_tb->first_block = p_temp_tb->useable_block = _require_new_block(tb_id);  //为本表申请一新块

	return 0;  //成功返回0
}

/*
   删除给定id号的表结构,相应的属性结构和记录数据
 */
int delete_tb(int tb_id)
{
	Del_Table_lock((ptr_tb+tb_id)->tb_name);

	if(tb_id<0 || tb_id>=NUM_OF_TB)
	  return -1;  //tb_id明显不正确，则返回－1
	TB *p_temp_tb;
	COLU *p_temp_colu;

	p_temp_tb=ptr_tb + tb_id;

	char temp[MAX_NAME];
	strcpy(temp,db_dir);
	strcat(temp,ptr_db->db_name);
	strcat(temp,"/");
	strcat(temp,p_temp_tb->tb_name);
	strcat(temp,".dat");
	rm_files(temp);

	p_temp_colu=ptr_colu + tb_id * NUM_OF_COLU * sizeof(COLU);

	/*删除第二区域中的相对应的表结构,只要将id置-1就表明删除*/
	p_temp_tb->id=-1;

	/*删除第二区域中的相对应表结构,也只要将id置-1*/
	int i;
	i=p_temp_tb->num_of_colu;
	while(i--)
	  p_temp_colu++->id=-1;

	BLOCK *p_temp_blk=ptr + p_temp_tb->first_block;
	while(1){  //删除属于这个表的每一块,置每一块的head结构中的tb为-1
		p_temp_blk->head.tb=-1;
		if(p_temp_blk->head.next != 0)
			p_temp_blk=ptr + p_temp_blk->head.next;
		else
			break;
	}

	return 0;
}

/*
   检查某表是否存在,传递参数是表的名称,如果存在，则返回此表的id，不存在则返回-1
 */
int ExistTable(char *tb_name)
{
	TB *p_temp_tb;
	p_temp_tb=ptr_tb;
	int i;
	for(i=0;i!=NUM_OF_TB;i++){
		if(!strcmp(p_temp_tb->tb_name,tb_name))
		  return p_temp_tb->id;  //返回tb的id,表示存在
		p_temp_tb++;
	}
	return -1;  //返回-1表示没找到，不存在
}


/*=============disk about==========*/
/*
   从磁盘上某个文件导入记录到当前数据库的某表，与用户交互
 */
int load_file()
{
	if(ptr == NULL){
		printf("no database in open\n");
		return -1;
	}

	printf("source files:");
	char file_name[MAX_LINE];
	fgets(file_name,MAX_LINE,stdin);
	file_name[strlen(file_name)-1]=0;
	FILE *fp;
	if((fp=fopen(file_name,"r"))==NULL){
		printf("open source file error: %s\n",file_name);
		return -2;
	}
	
	printf("to which table:");
	char tb_name[MAX_NAME];
	fgets(tb_name,MAX_NAME,stdin);
	tb_name[strlen(tb_name)-1]=0;
	int tb_id;
	if((tb_id=ExistTable(tb_name)) == -1){
		printf("no such table %s\n",tb_name);\
		return -1;
	}
	
	fseek(fp,0,SEEK_SET);
	
	char newline[MAX_LINE];
	struct timespec time_st;
	struct timespec time_end;
	clock_gettime(CLOCK_MONOTONIC,&time_st);
	printf("loading from %s...\n",file_name);
	while(fgets(newline,MAX_LINE,fp)!=NULL){
		if(!strcmp(newline,"\n"))
		  continue;
		newline[strlen(newline)-1]=0;
		if(insert_record(newline,tb_id)==-1){
			printf("insert error\n");
			return -1;
		}
		  
	}
	clock_gettime(CLOCK_MONOTONIC,&time_end);
	char re[MAX_LINE];
	print_time(re,1,time_st,time_end);
	return 0;
}

/*
   open()函数调用之,从磁盘文件导入表
 */
int load_record(int tb_id)
{
	FILE *fp;
	TB *p_temp_tb;
	p_temp_tb=ptr_tb + tb_id;
	char tb_name[MAX_NAME];
	
	printf("loading data from %s...\n",p_temp_tb->tb_name);

	strcpy(tb_name,db_dir);
	strcat(tb_name,ptr_db->db_name);
	strcat(tb_name,"/");
	strcat(tb_name,p_temp_tb->tb_name);
	strcat(tb_name,".dat");

	if((fp=fopen(tb_name,"r"))==NULL){
		printf("cannot open record file:%s\n",tb_name);
		return -1;
	}
	fseek(fp,0,SEEK_SET);   //打开文件并且将偏移量设为文件开头0
	char newline[MAX_LINE];
	struct timespec time_st;
	struct timespec time_end;
	clock_gettime(CLOCK_MONOTONIC,&time_st);
	while(fgets(newline,MAX_LINE,fp)!=NULL){
		newline[strlen(newline)-1]=0;
		if(insert_record(newline,tb_id)==-1){
			printf("insert error\n");
			return -2;
		}
	}
	clock_gettime(CLOCK_MONOTONIC,&time_end);
	char te[MAX_LINE];
	print_time(te,1,time_st,time_end);
	return 0;
}


/*
   open函数调用,选择打开数据库的时候初始化并导入数据
 */
int load_dict_and_init(char *db_name,char *u_pwd)
{
	
	FILE *fp;
	char newline[MAX_LINE];
	unsigned long db_size=0;
	if((fp=_open_dict_file(db_name,"r")) == NULL){
		printf("fail to open doct file\n");
		return -1;
	}
	
/*
   读取第一、二行,第一行内容是共享内存的大小,第二行内容是密码
   先检验密码是否正确,如果正确则利用第一行的数据来创建共享内存
 */

	if(fgets(newline,MAX_LINE,fp)==NULL){
		printf("fail to read the first line of the dict file\n");
		return -1;
	}

	db_size=atol(newline);
	
	if(fgets(newline,MAX_LINE,fp)==NULL){
		printf("fail to read the second line of the dict file\n");
		return -1;
	}
	char pwd[MAX_PWD];
	newline[strlen(newline)-1]=0;
	strcpy(pwd,newline);
	if(strcmp(pwd,u_pwd)){
		printf("password wrong\n");
		return -2;
	}
	
	int shmm_errno;
	shmm_errno=_get_init_shm(db_size);
	
	if(shmm_errno==-1){
		printf("fail to create shmm\n");
		return -3;
	}
	else if(shmm_errno==-2){
		printf("fail to binding shmm\n");
		return -4;
	}
	
	_insert_db_info(db_name,db_size,pwd); //写共享内存第一个区域

	char tag;  //tag记录将要或者目前正在读取的是什么内容,具体见下文
	int count=0;  //count记录每个表有多少个属性	
	int tb_id=0;

	while(fgets(newline,MAX_LINE,fp)!=NULL){
	/*如果tag是其中之一,则标记tag，然后读取下一行*/
		if(newline[0]=='#'||newline[0]=='@'){
			tag=newline[0];
			continue;
		}
		
		if(tag=='#'){   //tag是# 说明现在读取倒的是表名称
			if(count){
				TB *p_temp_tb=ptr_tb + tb_id;
				p_temp_tb->num_of_colu=count;
				p_temp_tb->first_block =p_temp_tb->useable_block =_require_new_block(tb_id);
				count=0;
			}
			newline[strlen(newline)-1]=0;
			
			Table_lock *tl = malloc(sizeof(Table_lock));
			Init_Table_lock(tl);
			strcpy(tl->Table_Name,newline);
			Add_Table_lock(tl);

			if((tb_id=_insert_tb_info(newline,-1)) == -1){
				printf("too many tables\n");
				return -1;;
			}
		} 
		if(tag=='@'){   //tag是@ 说明现在读取到的是列信息
			char colu_name[MAX_NAME];
			int length,type;
			
			newline[strlen(newline)-1]=0;
			strcpy(colu_name,newline);  //读属性名
			
			if(fgets(newline,MAX_LINE,fp)!=NULL)  //读属性限长
			  length=atoi(newline);
			if(fgets(newline,MAX_LINE,fp)!=NULL)  //读属性类型
			  type=atoi(newline);

			if(_insert_colu_info(tb_id ,colu_name,length,type)==-1){
				printf("too many fields\n");
				return -1;
			}
			count++;
		}
	}
	if(count){
		TB *p_temp_tb=ptr_tb + tb_id;
		p_temp_tb->num_of_colu=count;
		p_temp_tb->first_block=p_temp_tb->useable_block =_require_new_block(tb_id);
	}
	return 0;
}

/*====================record about=====================*/

/*获取给定表的第一条记录地址*/
record_add Get_First_Record(int tb_id)
{
	//printf("start Get_First_Record(int),tb_id=%d\n",tb_id);
	record_add first_record_add;
	TB *p_temp_tb=ptr_tb + tb_id;
	BLOCK *p_temp_blk=ptr + p_temp_tb->first_block;
	first_record_add.p_blk=p_temp_blk;
	first_record_add.off=0;  //块内第一条记录，是data[0],所以off是0

	if(first_record_add.p_blk->data[ first_record_add.off ] == '\0')  //如果记录是0,则找下一条不为空的记录
	  first_record_add=Get_Next_Add(first_record_add);

	//printf("finish Get_First_Record()\n");
	return first_record_add;
}
//给定一记录地址,返回其下一条不为空的记录的地址
record_add Get_Next_Add(record_add rd)
{
	record_add next_rd;
	next_rd.p_blk=rd.p_blk;
	next_rd.off=rd.off + rd.p_blk->head.record_size;
	while(1){
		if( (next_rd.off + rd.p_blk->head.record_size) >= (BLOCK_SIZE - sizeof(BLOCK_HEAD)) ){
		//如果这条记录是本块内最后一条记录,则下一条记录在相关联的下一块块内
			if(next_rd.p_blk->head.next == 0){  // 最后一块了
				next_rd.p_blk=NULL;  //如果是最后一块,则设p_blk为NULL,然后马上返回
				break;
			}
			next_rd.p_blk = ptr + next_rd.p_blk->head.next;  //获取相关联的下一块的地址
			next_rd.off=0;
		}
		if(next_rd.p_blk->data[next_rd.off] != 0)  //如果找到的下一条记录不为空,则返回这条记录的地址,否则继续查找
		  break;
		else
		  next_rd.off += rd.p_blk->head.record_size;
	}
	return next_rd;
}
/*根据给定的记录地址(record_add),填充record结构,返回下一条记录的地址*/
record_add Get_Record(record_add rd_addr,Record *record)
{
	
	BLOCK *p_temp_blk=rd_addr.p_blk;  //记录所在块的块地址
	int off=rd_addr.off;  //记录在块内数据部分的偏移量,即data[off]
	int tb_id=p_temp_blk->head.tb;  //获取所属表的id
	int num_of_colu=(ptr_tb + tb_id)->num_of_colu;  //表有多少个属性
	
	char rd[MAX_LINE],*p_rd;  //临时存放记录
	char *sp[NUM_OF_COLU];  //sp存放记录的每一个字段
	bzero(sp,NUM_OF_COLU);
	p_rd=rd;  //p_rd指向临时存放记录的数组
	strcpy(rd,&(p_temp_blk->data[off]));  //将本次处理的记录复制到rd数组内
	int i;

	///////////////////////
   	pthread_mutex_lock(&fun_lock);
	for(i=0;;i++){
		sp[i]=strtok(p_rd,SEP);
		if(sp[i]==NULL)
		  break;
		p_rd=NULL;
	}

	pthread_mutex_unlock(&fun_lock);
	
	if(i != num_of_colu){   //记录的属性数目和表规定的不符，有可能是load的时候的错误。
		record_add error_rd;
		error_rd.p_blk=NULL;
		printf("an error");
		return error_rd;
	}

	Field *p_field;
	for(p_field=record->Field_list;;p_field=p_field->Next_Field){ //外层循环,每次循环处理一个field
		int j,colu_id;
		COLU *p_temp_colu;
		//内存循环,找到相应field的colu_id
		for(j=0,p_temp_colu=ptr_colu+NUM_OF_COLU*tb_id;j!=num_of_colu;j++,p_temp_colu++){
			if(!strcmp(p_field->Field_Name,p_temp_colu->colu_name)){
				colu_id=p_temp_colu->id;
				break;
			}
		}
		//以上为找到相应column,并记录其的colu_id,下面开始处理记录中的相应column
		//printf("attention : found column_id is : %d\n",colu_id);

		//找到相应field的colu_id,开始填充p_field
		if(p_temp_colu->type==0){  //0是string,
			p_field->date_type=STRINGX;
			strcpy(p_field->Str_Val,sp[colu_id]);
		}
		else if(p_temp_colu->type==1){  //1是int
			p_field->date_type=INTX;
			p_field->Int_Val=atoi(sp[colu_id]);
		}
		else{  //2是float
			p_field->date_type=DOUBLEX;
			p_field->Double_Val=atof(sp[colu_id]);
		}
		//填充完毕

		if(p_field->Next_Field == NULL)
		  break;
	}
	/////////////处理完毕////////////
	/*查找下一条记录的地址以供返回*/
	record_add next_rd;
	next_rd.p_blk=p_temp_blk;
	next_rd.off=off;
	next_rd=Get_Next_Add(next_rd);
	
	return next_rd;
}

//给定一个记录的地址,删除之
void Delete_Record(record_add rd)
{
	BLOCK *p_temp_blk=rd.p_blk;
	int off=rd.off;
	p_temp_blk->data[off]=0;  //标记为0代表删除

	//删除完这条记录后,将本块的useable_offset修改为本条记录的偏移量
	p_temp_blk->head.unused++;
	p_temp_blk->head.useable_offset=off;

	int total_rd=(BLOCK_SIZE -sizeof(BLOCK_HEAD))/(p_temp_blk->head.record_size);
	if( p_temp_blk->head.unused == total_rd ){  //如果块空了，则释放掉
		unsigned long pre=p_temp_blk->head.pre;
		unsigned long next=p_temp_blk->head.next;
		TB *p_temp_tb=ptr_tb + p_temp_blk->head.tb;

		if(pre == 0 && next != 0){  //如果是第一块
			p_temp_tb->first_block=next;
			record_add next_add;
			next_add.p_blk=ptr + next;
			next_add.off=0;
			find_free_space(p_temp_blk->head.tb,next_add);
		}

		if(pre != 0 && next == 0){
			BLOCK *p_temp_blk2=ptr + pre;
			p_temp_blk2->head.next=0;
		}

		if( pre!=0 && next!=0){
			BLOCK *p_temp_blkpre=ptr + pre;
			BLOCK *p_temp_blknext=ptr + next;

			p_temp_blkpre->head.next=next;
			p_temp_blknext->head.pre=pre;
		}
		
		if(pre != 0 || next != 0)  //如果这个表就这么一个块,那么即使这个块中没有数据了，这个表也还是不能删这个块
			p_temp_blk->head.tb=-1;
	}
}

//将Record类型的记录合并成char *,插入一条记录的时候先调用本函数生成一个char *记录,再调用insert_record()函数
void Combine_Record_To_String(Record *record,char *str)
{
	Field *p_field=record->Field_list;
	int i=0;
	for(;;){
		if(p_field->date_type==STRINGX){
			if(i==0)
			  strcpy(str,p_field->Str_Val);
			else
			  strcat(str,p_field->Str_Val);
		}
		else if(p_field->date_type==INTX){
			char temp[MAX_LINE];
			sprintf(temp,"%d",p_field->Int_Val);
			if(i==0)
			  strcpy(str,temp);
			else
			  strcat(str,temp);
		}
		else{
			char temp[MAX_LINE];
			sprintf(temp,"%f",p_field->Double_Val);
			if(i==0)
			  strcpy(str,temp);
			else
			  strcat(str,temp);
		}
		i=1;
		if(p_field->Next_Field==NULL)
		  break;
		strcat(str,SEP);  //插入分隔符
		p_field=p_field->Next_Field;
	}
	//处理完的记录在str中
}

int find_free_space(int tb_id,record_add curr)
{
	BLOCK *p_temp_blk=curr.p_blk;
	int off=curr.off;
	TB *p_temp_tb=ptr_tb + tb_id;
	int flag_found=0;  //找到可插入空闲块为1，没找到为0

	if( p_temp_blk->head.unused > 0){  //如果这个块还有剩余,则只需要修改useable_offset就可以
		int i=0;
		while(1){
			if(p_temp_blk->data[i] == 0){
				flag_found=1;
				p_temp_blk->head.useable_offset=i;
				break;
			}
			i+=p_temp_blk->head.record_size;
		}
	}
	else{  //块内没有剩余,则查找一个新块
		BLOCK *p_temp_blk2=p_temp_blk;
		while(1){
			if(!p_temp_blk2->head.next)  //如果是最后一块,则从头开始查
				p_temp_blk2=ptr + (ptr_tb + p_temp_blk2->head.tb)->first_block;
			else  //不是最后一块,则修改为相关连的下一块
				p_temp_blk2=ptr + p_temp_blk2->head.next;
			

			if(p_temp_blk2 == p_temp_blk) //如果找了一圈没找到,则跳出,申请一块新的
			  break;
                      
			if( p_temp_blk2->head.unused){ 
				flag_found=1;
				p_temp_tb->useable_block=(void *)p_temp_blk2 - ptr;
				break;
			}
		}

		if(!flag_found){  //如果没找到,则申请新块
			unsigned long new_block=_require_new_block(tb_id);
			if(new_block == 1)
				return -1;  //内存满
			unsigned long temp;
			temp=p_temp_tb->first_block;
			
			BLOCK *p_temp_blk2,*p_temp_blk3;
			p_temp_blk2=ptr + new_block;   //新申请的块
			p_temp_blk3=ptr + ptr_tb->first_block; //原来的第一块
			
			p_temp_blk3->head.pre=new_block; //原来第一块的pre修改为新申请块的偏移量
			p_temp_blk2->head.next=temp; //新申请的块的next修改为原来的第一块的偏移量
			
			p_temp_tb->first_block=new_block;
			p_temp_tb->useable_block=new_block;
		}
	}
}
/*
   插入一条记录
   根据所提供的TB的id号,找出此表中可用的块，如果所有的块都满了，则申请一个新块
 */
int insert_record(char *content,int tb_id)
{
	if(tb_id<0 || tb_id>=NUM_OF_TB)
	  return -1;
	TB *ptr_temp_tb=ptr_tb + tb_id;
	BLOCK *ptr_temp_blk=ptr + ptr_temp_tb->useable_block;  //获得可用块的指针
	int off=ptr_temp_blk->head.useable_offset;

	unsigned long rd_size=ptr_temp_blk->head.record_size;  //要插入的记录的长度比规定的长，则返回-1
	if(strlen(content) > rd_size)
	  return -1;
	
	strcpy(&ptr_temp_blk->data[off],content); //将记录复制到块内
	ptr_temp_blk->head.unused--;
	record_add next;
	next.p_blk=ptr_temp_blk;
	next.off=off;
	if(find_free_space(tb_id,next)==-1)
	  return -1;
	return 0;
}

/*更新一条指定的记录*/
void Update_Record(record_add rd,Record *new_rd)
{
	//printf("start Update_Record(record_add,Record *)\n");
	BLOCK *p_temp_blk=rd.p_blk;
	int off=rd.off;

	char temp_temp[MAX_LINE];
	bzero(temp_temp,MAX_LINE);
	Combine_Record_To_String(new_rd,temp_temp);

	strcpy(&p_temp_blk->data[off],temp_temp);
}


/*==================field about==================*/


/*检查某表的某列是否存在,先检查给定表名是否存在,不存在则返回-1,再检查列是否存在,不存在返回-2,存在则返回列id*/
int ExistField(char *tb_name,char *field_name)
{
//	printf("start ExistField(char *,char *),tb_name=%s,field_name=%s\n",tb_name,field_name);
	int tb_id=ExistTable(tb_name);
	if(tb_id==-1)
	  return -1;  //表不存在,则返回-1
	
	COLU *p_temp_colu;
	p_temp_colu = ptr_colu + NUM_OF_COLU*tb_id;
	int i;
	for(i=0;i!=NUM_OF_COLU;i++){
		if(!strcmp(p_temp_colu->colu_name,field_name))
		  return p_temp_colu->id; //存在则返回列id
		p_temp_colu++;
	}
//	printf("fielnae=%s\n",field_name);
//	printf("finish ExistField(),error -2\n");
	return -2;  //不存在则返回-2
}


int Field_DateType(char *tb_name,char *f_name)
{
	int tb_id,f_id;
	if((tb_id=ExistTable(tb_name))==-1)
	  return -1;  //表不存在返回-1
	if((f_id=ExistField(tb_name,f_name))==-2)
	  return -2;  //列不存在返回-2
	COLU *p_temp_colu=ptr_colu + tb_id*NUM_OF_COLU + f_id;
	if(p_temp_colu->type==0)
	  return STRINGX;
	else if(p_temp_colu->type==1)
	  return INTX;
	else
	  return DOUBLEX;
}

void  Get_Field_Content(record_add rd_addr,int field_id,Field *f)
{
	
	BLOCK *p_temp_blk=rd_addr.p_blk;  //记录所在块的块地址
	int off=rd_addr.off;  //记录在块内数据部分的偏移量,即data[off]
	char rd[MAX_LINE],*p_rd;  //临时存放记录
	char *sp[NUM_OF_COLU];  //sp存放记录的每一个字段
	bzero(sp,NUM_OF_COLU);

	p_rd=rd;  //p_rd指向临时存放记录的数组

	strcpy(rd,&(p_temp_blk->data[off]));  //将本次处理的记录复制到rd数组内


	//将rd（一条记录）用strtok函数进行分割，存放在sp中
	int i;
	for(i=0;;i++){
		sp[i]=strtok(p_rd,SEP);
		if(sp[i]==NULL)
		  break;
		p_rd=NULL;
	}

	COLU *p_temp_colu=ptr_colu + NUM_OF_COLU*(p_temp_blk->head.tb) + field_id;

	strcpy(f->Field_Name,p_temp_colu->colu_name);

	f->str_len=p_temp_colu->length;
	
	if(p_temp_colu->type==0){  //0是string,
		f->date_type=STRINGX;
		strcpy(f->Str_Val,sp[field_id]);
	}
	else if(p_temp_colu->type==1){  //1是int
		f->date_type=INTX;
		f->Int_Val=atoi(sp[field_id]);
	}
	else{  //2是float
		f->date_type=DOUBLEX;
		f->Double_Val=atof(sp[field_id]);
	}
	//printf("finish Get_Field_Content(record_add,int,Field *)\n");
}

int Get_Field_Num(int tb_id)
{
	return (ptr_tb+tb_id)->num_of_colu;
}

int Get_Field(int tb_id,int field_id,Field *f)
{
	
	COLU *p=ptr_colu + NUM_OF_COLU*tb_id + field_id;
	//printf("p->colu_name=%s\n",p->colu_name);
	strcpy(f->Field_Name,p->colu_name);
	if(p->type==0)
	  f->date_type=STRINGX;
	else if(p->type==1)
	  f->date_type=INTX;
	else
	  f->date_type=DOUBLEX;
	f->str_len=p->length;
	//printf("finish Get_Field(int,int,Field)\n");
	return 1;
}

/*================system about====================*/
int change_pwd()
{
	if(ptr == NULL){
		printf("no database in open\n");
		return -1;
	}

	char pwd[MAX_PWD];
	char pwd_2[MAX_PWD];
	printf("new password:");
	fgets(pwd,MAX_PWD,stdin);
	pwd[strlen(pwd)-1]=0;
	if(_check_input(reg_pwd,pwd)){ 
		printf("input error:illegal password\n");
		return -1;
	}
	printf("again:");
	fgets(pwd_2,MAX_PWD,stdin);
	pwd_2[strlen(pwd_2)-1]=0;
	if(!strcmp(pwd,pwd_2)){
		strcpy(ptr_db->pwd,pwd);
		printf("password has changed\n");
		return 0;
	}
	else{
		printf("not same\n");
		return -1;
	}
}

/*
   移除当前打开着的数据库,其实就是移除共享内存
 */
int remove_db()
{
	if(ptr == NULL)
	  return -1;
	
	if(shmdt(ptr)!=0){
		printf("disconnect shm error\n");
		return -1;
	}
	if(shmctl(shmid,IPC_RMID,0)!=0){
		printf("rm shm error\n");
		return -1;
	}
	
	ptr=NULL;
	
	return 0;
}

void quit_sys()
{
	remove_db();
	close(listenfd);
	exit(0);
}

int save_record_to_disk(int tb_id)
{
	FILE *fp;
	char file_name[MAX_LINE];
	TB *p_temp_tb=ptr_tb + tb_id;
	strcpy(file_name,db_dir);  // db_files/
	strcat(file_name,ptr_db->db_name);  // db_files/my_db
	strcat(file_name,"/");  // db_files/my_db/
	strcat(file_name,p_temp_tb->tb_name);  // db_files/my_db/list
	strcat(file_name,".dat");
	
	
	if((fp=fopen(file_name,"w+")) == NULL)
	  return -2;
	fseek(fp,0,SEEK_SET);

	char newline[MAX_LINE];
	record_add rd_add=Get_First_Record(tb_id);
	if(rd_add.p_blk == NULL)
	  return 0;
	BLOCK *p_temp_blk;
	unsigned long off;
	while(1){
		p_temp_blk=rd_add.p_blk;
		off=rd_add.off;
		strcpy(newline,&(p_temp_blk->data[off]));
		fprintf(fp,"%s\n",newline);
		rd_add=Get_Next_Add(rd_add);
		if(rd_add.p_blk==NULL)
		  break;
	}
	fclose(fp);
	return 0;
}

int save_all(int sockfd)
{
	char newline[MAX_COMMAND];
	if(ptr == NULL){
		strcpy(newline,"no database in open\n");
		if(sockfd != 1)
		  strcat(newline,"\n\n\n");
		write(sockfd,newline,strlen(newline));
		return -1;
	}
	if(_save_zone123_to_disk() != 0){
		strcpy(newline,"save dict error\n");
		if(sockfd != 1)
		  strcat(newline,"\n\n\n");
		write(sockfd,newline,strlen(newline));
		return -1;
	}
	int i=0;
	TB  *p_temp_tb=ptr_tb;
	for(;i!=NUM_OF_TB;i++,p_temp_tb++){
		if(p_temp_tb->id==-1)
		  continue;
		int j;
		if((j=save_record_to_disk(p_temp_tb->id)) != 0){
			char newline[MAX_COMMAND];
			sprintf(newline,"save record to table %s error\n",p_temp_tb->tb_name);
			if(sockfd != 1)
				strcat(newline,"\n\n\n");
			write(sockfd,newline,strlen(newline));
			return -2;
		}
	}
	time(&last_save_time);
	strcpy(newline,"save ok\n");
	if(sockfd != 1)
		strcat(newline,"\n\n\n");
	write(sockfd,newline,strlen(newline));
	return 0;
}


//整体数据库信息
int db_info(int sockfd)
{
	char newline[MAX_COMMAND],temp[MAX_COMMAND];
	if(ptr == NULL){
		sprintf(newline,"no database in open\n");
		if(sockfd != 1)
		  strcat(newline,"\n\n\n");
		write(sockfd,newline,strlen(newline));
		return -1;
	}

	sprintf(newline,"\ndatabase infomation:----------------------------------------------------------------------------\n");
	sprintf(temp,"database name:%s\npassword:%s\n",ptr_db->db_name,ptr_db->pwd);
	strcat(newline,temp);
	bzero(temp,MAX_COMMAND);
	sprintf(temp,"system start at:%s",ctime(&system_start));
	strcat(newline,temp);
	bzero(temp,MAX_COMMAND);
	if(last_save_time == system_start)
	  sprintf(temp,"last save time:null\n");
	else
	  sprintf(temp,"last save time:%s",ctime(&last_save_time));
	strcat(newline,temp);
	bzero(temp,MAX_COMMAND);
	strcat(newline,"----------------------------------------------------------------------------------------------------\n");
	unsigned long db_total_size=sizeof(DB);
	TB *curr_tb=ptr_tb;
	COLU *curr_colu;
	int i;
	for(i=0;i!=NUM_OF_TB;i++,curr_tb++){
		if(curr_tb->id == -1)
		  continue;
		sprintf(temp,"table id:%d,table name:%s,column number:%d,first_block=%p,useable_block=%p\n",curr_tb->id,curr_tb->tb_name,curr_tb->num_of_colu,ptr+curr_tb->first_block,ptr+curr_tb->useable_block);
		strcat(newline,temp);
		bzero(temp,MAX_COMMAND);
		curr_colu=ptr_colu + curr_tb->id * NUM_OF_COLU;
		int j;
		for(j=0;j!=NUM_OF_COLU;j++,curr_colu++){
			if(curr_colu->id == -1)
			  continue;
			sprintf(temp,"--column id:%d,column name:%s,length:%d",curr_colu->id,curr_colu->colu_name,curr_colu->length);
			if(curr_colu->type == 0)
			  strcat(temp,",type:varchar\n");
			else if(curr_colu->type ==1 )
			  strcat(temp,",type:integer\n");
			else
			  strcat(temp,",type:double\n");
			strcat(newline,temp);
			bzero(temp,MAX_COMMAND);
		}
		unsigned long blk_count=0,rd_count=0,free_rd_count=0;
		tb_block_numbers(curr_tb->id, &blk_count, &rd_count, &free_rd_count);
		sprintf(temp,"table has %lu blocks,%lu records,%lu free records\n",blk_count,rd_count,free_rd_count);
		strcat(newline,temp);
		bzero(temp,MAX_COMMAND);
		unsigned long tb_total_size=sizeof(TB) + sizeof(COLU)*NUM_OF_COLU + BLOCK_SIZE*blk_count;
		db_total_size += tb_total_size;
		sprintf(temp,"table takes total %lu bytes(%f KB) memory\n",tb_total_size,(double)(tb_total_size)/1024);
		strcat(newline,temp);
		bzero(temp,MAX_COMMAND);
		strcat(newline,"--------------------------------------------------------------------------------------------\n");
	}
	sprintf(temp,"database size is :%lu bytes,it takes %lu bytes present\n",ptr_db->db_size+SIZE_ALL_ZONE,db_total_size);
	strcat(newline,temp);
	if(sockfd != 1)
	  strcat(newline,"\n\n\n");
	write(sockfd,newline,strlen(newline));
	return 0;
}

//查询某一表占用的块的信息
int tb_block_numbers(int tb_id,unsigned long *blk_count,unsigned long *rd_count,unsigned long *free_rd_count)
{
	TB *p_temp_tb=ptr_tb + tb_id;
//	if(p_temp_tb->first_block == -1){
//		*blk_count=*rd_count=*free_rd_count=0;
//		return -1;
//	}
	BLOCK *p_temp_blk=ptr + p_temp_tb->first_block;
	int off=0;
	unsigned long next;
	while(1){
		if( (next = off + p_temp_blk->head.record_size) >= (BLOCK_SIZE - sizeof(BLOCK_HEAD)) ){
			(*blk_count)++;
			if(p_temp_blk->head.next == 0)
			  break;
			p_temp_blk = ptr + p_temp_blk->head.next;
			off=0;
			next=p_temp_blk->head.record_size;
		}
		if(p_temp_blk->data[off] == 0)
		  (*free_rd_count)++;
		else
		  (*rd_count)++;
		off=next;
	}
	return 0;
}


void see_help(int sockfd)
{
	char newline[MAX_COMMAND];
	strcpy(newline,"------------------------------------------------\n");
	strcat(newline,"1.help:see command details\n");
	strcat(newline,"2.info:see current database infomation\n");
	strcat(newline,"3.save:save data to disk\n");
	if(sockfd != 1){	
		strcat(newline,"4.bye:disconnect from server\n");
	strcat(newline,"------------------------------------------------\n");
		strcat(newline,"\n\n\n");
	}
	else{
		strcat(newline,"4.passwd:change password\n");
		strcat(newline,"5.open:open a database\n");
		strcat(newline,"6.createdb:create a database\n");
		strcat(newline,"7.load:load a disk data file into a table\n");
		strcat(newline,"8.removedb:remove current database\n");
		strcat(newline,"9.quit:quit system\n");
		strcat(newline,"10.deletedb:delete current database and its file on disk\n");
		strcat(newline,"------------------------------------------------\n");
	}
	write(sockfd,newline,strlen(newline));
}
